//===============================================================================
// Microsoft patterns & practices
// Web Client Software Factory
//-------------------------------------------------------------------------------
// Copyright (C) Microsoft Corporation.  All rights reserved.
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE.
//-------------------------------------------------------------------------------
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious.  No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
//===============================================================================
using System;
using System.Data;
using System.Threading;
using Microsoft.Practices.EnterpriseLibrary.Data;
using WCSFContrib.PageFlow.Storage.EnterpriseLibrary.Tests.Mocks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.ObjectModel;
using System.Collections.Generic;

namespace WCSFContrib.PageFlow.Storage.EnterpriseLibrary.Tests
{
    [TestClass]
    public class PageFlowInstanceCorrelationSqlProviderFixture
    {
        [ClassInitialize]
        public static void SetupDatabase(TestContext testContext)
        {
            SetupDatabaseUtility.Setup();
        }

        private const string DataBaseName = "PageFlowInstanceStoreProvider";

        [TestCleanup]
        public void Cleanup()
        {
            Database db = DatabaseFactory.CreateDatabase(DataBaseName);
            db.ExecuteReader(CommandType.Text, "TRUNCATE TABLE pageFlow_InstanceStore");
        }

        [TestMethod]
        public void CanCreateNew()
        {
            IPageFlowCorrelationTokenProvider tokenProvider = new MockPageFlowCorrelationTokenProvider();
            IPageFlowInstanceStore store = new PageFlowInstanceCorrelationSqlProvider(DataBaseName, tokenProvider);
            Assert.IsTrue(store is IPageFlowInstanceStore);
        }

        [TestMethod]
        public void CanStoreInstance()
        {
            MockPageFlowCorrelationTokenProvider tokenProvider = new MockPageFlowCorrelationTokenProvider();
            IPageFlowInstanceStore store = new PageFlowInstanceCorrelationSqlProvider(DataBaseName, tokenProvider);
            MockPageFlowInstance pageFlow = new MockPageFlowInstance();
            pageFlow.Id = Guid.NewGuid();

            store.Add(pageFlow);

            Database db = DatabaseFactory.CreateDatabase(DataBaseName);
            using (
                IDataReader reader =
                    db.ExecuteReader(CommandType.Text,
                                     string.Format("SELECT * FROM pageFlow_InstanceStore WHERE InstanceId = '{0}'", pageFlow.Id)))
            {
                reader.Read();
                Assert.IsTrue(tokenProvider.GetCorrelationTokenCalled);
                Assert.AreEqual(pageFlow.Definition.PageFlowType.AssemblyQualifiedName, reader.GetString(1));
                Assert.AreEqual(pageFlow.Id, reader.GetGuid(0));
                Assert.AreEqual(false, reader.GetBoolean(3));
                Assert.AreEqual(tokenProvider.GetCorrelationToken(), reader.GetString(2));
            }
        }

        [TestMethod]
        public void CanRemoveInstance()
        {
            MockPageFlowCorrelationTokenProvider tokenProvider = new MockPageFlowCorrelationTokenProvider();
            IPageFlowInstanceStore store = new PageFlowInstanceCorrelationSqlProvider(DataBaseName, tokenProvider);
            MockPageFlowInstance pageFlow = new MockPageFlowInstance();
            pageFlow.Id = Guid.NewGuid();
            store.Add(pageFlow);

            store.Remove(pageFlow);

            Database db = DatabaseFactory.CreateDatabase(DataBaseName);
            int count = 0;
            using (
                IDataReader reader =
                    db.ExecuteReader(CommandType.Text,
                                     string.Format("SELECT * FROM pageFlow_InstanceStore WHERE InstanceId = '{0}'", pageFlow.Id)))
            {
                while (reader.Read())
                {
                    count++;
                }
            }
            Assert.AreEqual(0, count);
        }

        [TestMethod]
        public void ShouldReturnLastRunningInstance()
        {
            IPageFlowCorrelationTokenProvider tokenProvider = new MockPageFlowCorrelationTokenProvider();
            IPageFlowInstanceStore store = new PageFlowInstanceCorrelationSqlProvider(DataBaseName, tokenProvider);
            MockPageFlowInstance pageFlow = new MockPageFlowInstance();
            pageFlow.Id = Guid.NewGuid();
            store.Add(pageFlow);
            store.SetPageFlowRunning(pageFlow);

            Guid instanceId = store.GetLastRunningInstance();
            Assert.AreEqual(instanceId, pageFlow.Id);
        }

        [TestMethod]
        public void ShouldReturnEmptyGuidIfNotRunningInstance()
        {
            IPageFlowCorrelationTokenProvider tokenProvider = new MockPageFlowCorrelationTokenProvider();
            IPageFlowInstanceStore store = new PageFlowInstanceCorrelationSqlProvider(DataBaseName, tokenProvider);

            Guid instanceId = store.GetLastRunningInstance();
            Assert.AreEqual(Guid.Empty, instanceId);
        }

        [TestMethod]
        public void ShouldSetRunningFalseWhenCallingSetPageFlowNotRunning()
        {
            IPageFlowCorrelationTokenProvider tokenProvider = new MockPageFlowCorrelationTokenProvider();
            IPageFlowInstanceStore store = new PageFlowInstanceCorrelationSqlProvider(DataBaseName, tokenProvider);
            IPageFlow instance = new MockPageFlowInstance();
            store.Add(instance);
            store.SetPageFlowNotRunning(instance);

            Database db = DatabaseFactory.CreateDatabase(DataBaseName);
            int count = 0;
            using (
                IDataReader reader =
                    db.ExecuteReader(CommandType.Text,
                                     string.Format("SELECT * FROM pageFlow_InstanceStore WHERE InstanceId = '{0}' and Running=0", instance.Id)))
            {
                while (reader.Read())
                {
                    count++;
                }
            }
            Assert.AreEqual(1, count);
        }

        [TestMethod]
        public void ShouldSetRunningTrueWhenCallingSetPageFlowRunning()
        {
            IPageFlowCorrelationTokenProvider tokenProvider = new MockPageFlowCorrelationTokenProvider();
            IPageFlowInstanceStore store = new PageFlowInstanceCorrelationSqlProvider(DataBaseName, tokenProvider);
            IPageFlow instance = new MockPageFlowInstance();
            store.Add(instance);
            store.SetPageFlowRunning(instance);

            Database db = DatabaseFactory.CreateDatabase(DataBaseName);
            int count = 0;
            using (
                IDataReader reader =
                    db.ExecuteReader(CommandType.Text,
                                     string.Format("SELECT * FROM pageFlow_InstanceStore WHERE InstanceId = '{0}' and Running = 1", instance.Id)))
            {
                while (reader.Read())
                {
                    count++;
                }
            }
            Assert.AreEqual(1, count);
        }

        [TestMethod]
        public void ShouldRemoveInstanceFromStoreWhenCallRemove()
        {
            IPageFlowCorrelationTokenProvider tokenProvider = new MockPageFlowCorrelationTokenProvider();
            IPageFlowInstanceStore store = new PageFlowInstanceCorrelationSqlProvider(DataBaseName, tokenProvider);
            IPageFlow instance = new MockPageFlowInstance();
            store.Add(instance);
            store.Remove(instance.Id);

            Database db = DatabaseFactory.CreateDatabase(DataBaseName);
            int count = 0;
            using (
                IDataReader reader =
                    db.ExecuteReader(CommandType.Text,
                                     string.Format("SELECT * FROM pageFlow_InstanceStore WHERE InstanceId = '{0}'", instance.Id)))
            {
                while (reader.Read())
                {
                    count++;
                }
            }
            Assert.AreEqual(0, count);
        }

        [TestMethod]
        public void ShouldReturnEmptyGuidWhenCallingGetByTypeAndInstanceIsNotOnTheStore()
        {
            IPageFlowCorrelationTokenProvider tokenProvider = new MockPageFlowCorrelationTokenProvider();
            IPageFlowInstanceStore store = new PageFlowInstanceCorrelationSqlProvider(DataBaseName, tokenProvider);

            Assert.AreEqual(Guid.Empty, store.GetByType(typeof(MockPageFlowType)));

        }

        [TestMethod]
        public void ShouldReturnInstanceByTypeWhenWorkflowTypeIsOnStore()
        {
            IPageFlowCorrelationTokenProvider tokenProvider = new MockPageFlowCorrelationTokenProvider();
            IPageFlowInstanceStore store = new PageFlowInstanceCorrelationSqlProvider(DataBaseName, tokenProvider);
            store.Add(new MockPageFlowInstance());
            Assert.IsNotNull(store.GetByType(typeof(MockPageFlowType)));

        }

        [TestMethod]
        public void CanNestPageFlows()
        {
            MockPageFlowFactory factory = new MockPageFlowFactory();
            IPageFlowCorrelationTokenProvider tokenProvider = new MockPageFlowCorrelationTokenProvider();
            IPageFlowInstanceStore store = new PageFlowInstanceCorrelationSqlProvider(DataBaseName, tokenProvider);

            MockPageFlowInstance firstInstance = new MockPageFlowInstance();
            firstInstance.Id = Guid.NewGuid();
            SecondMockPageFlowInstance secondInstance = new SecondMockPageFlowInstance();
            secondInstance.Id = Guid.NewGuid();

            store.Add(firstInstance);
            store.SetPageFlowRunning(firstInstance);

            store.Add(secondInstance);
            store.SetPageFlowRunning(secondInstance);

            Guid running = store.GetLastRunningInstance();
            Assert.AreEqual(secondInstance.Id, running);
        }

        [TestMethod]
        public void ShouldGetInstanceTypeForAnInstanceId()
        {
            IPageFlowCorrelationTokenProvider tokenProvider = new MockPageFlowCorrelationTokenProvider();
            IPageFlowInstanceStore store = new PageFlowInstanceCorrelationSqlProvider(DataBaseName, tokenProvider);
            MockPageFlowInstance instance = new MockPageFlowInstance();
            instance.Id = Guid.NewGuid();
            store.Add(instance);

            string type = store.GetInstanceType(instance.Id);

            Assert.AreEqual(instance.Definition.PageFlowType.AssemblyQualifiedName, type);
        }

        [TestMethod]
        public void ShouldReturnNullWhenGetInstanceTypeForANonExistingInstance()
        {
            IPageFlowCorrelationTokenProvider tokenProvider = new MockPageFlowCorrelationTokenProvider();
            IPageFlowInstanceStore store = new PageFlowInstanceCorrelationSqlProvider(DataBaseName, tokenProvider);
            MockPageFlowInstance instance = new MockPageFlowInstance();
            instance.Id = Guid.NewGuid();

            string type = store.GetInstanceType(instance.Id);

            Assert.IsNull(type);
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void GetInstanceTypeWithEmptyIdThrows()
        {
            IPageFlowCorrelationTokenProvider tokenProvider = new MockPageFlowCorrelationTokenProvider();
            IPageFlowInstanceStore store = new PageFlowInstanceCorrelationSqlProvider(DataBaseName, tokenProvider);
            store.GetInstanceType(Guid.Empty);
        }
    }

    internal class SecondMockPageFlowInstance : MockPageFlowInstance
    {
    }

    internal class SecondMockPageFlowDefinition : IPageFlowDefinition
    {
        #region IPageFlowDefinition Members

        public AbandonBehavior Abandonable
        {
            get { throw new Exception("The method or operation is not implemented."); }
        }

        public Page AbortPage
        {
            get { throw new Exception("The method or operation is not implemented."); }
        }

        public bool Constrained
        {
            get { throw new Exception("The method or operation is not implemented."); }
        }

        public string Name
        {
            get { throw new Exception("The method or operation is not implemented."); }
        }

        public string NotRunningRedirect
        {
            get { throw new Exception("The method or operation is not implemented."); }
        }

        public ReadOnlyCollection<Page> Pages
        {
            get { throw new Exception("The method or operation is not implemented."); }
        }

        public Page StartPage
        {
            get { throw new Exception("The method or operation is not implemented."); }
        }

        public Type PageFlowType
        {
            get { return typeof(MockPageFlowType); }
        }

        public bool ContainsPage(Page page)
        {
            throw new Exception("The method or operation is not implemented.");
        }

        public bool ContainsUrl(string url)
        {
            throw new Exception("The method or operation is not implemented.");
        }

        public Page GetPage(string stateName)
        {
            throw new Exception("The method or operation is not implemented.");
        }

        public Page GetPageFromUrl(string url)
        {
            throw new Exception("The method or operation is not implemented.");
        }

        #endregion

    }


    public class MockPageFlowFactory : IPageFlowFactory
    {
        #region IPageFlowFactory Members
        public Type TypeToCreate = typeof(MockPageFlowInstance);

        public IPageFlow GetPageFlow(Type pageFlowType, Guid instanceId)
        {
            MockPageFlowInstance instance;

            instance = (MockPageFlowInstance)Activator.CreateInstance(TypeToCreate);

            instance.Id = instanceId;
            return instance;
        }

        public IPageFlow GetPageFlow(Type pageFlowType)
        {
            throw new NotImplementedException();
        }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            throw new Exception("The method or operation is not implemented.");
        }

        #endregion
    }
}
